

#include "tca9554a.h"

static char *TAG = "TCA9554A";

#define SET_BITS(_m, _s, _v) ((_v) ? (_m) | ((_s)) : (_m) & ~((_s)))
#define GET_BITS(_m, _s) (((_m) & (_s)) ? true : false)

#define TCA9554_SENSOR_ADDR 0x38
#define TCA9554_INPUT_PORT 0x00
#define TCA9554_OUTPUT_PORT 0x01
#define TCA9554_POLARITY_INVERSION_PORT 0x02
#define TCA9554_CONFIGURATION_PORT 0x03

typedef struct
{
    uint8_t addr;
    char *name;
} tca9554_dev_t;

esp_err_t tca9554_write_reg(uint8_t reg_addr, uint8_t data)
{
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (TCA9554_SENSOR_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);
    i2c_master_write_byte(cmd, data, true);
    i2c_master_stop(cmd);
    ESP_RETURN_ON_ERROR(i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS), TAG, "I2C write_reg failed");
    i2c_cmd_link_delete(cmd);
    return ESP_OK;
}

uint8_t tca9554_read_reg(uint8_t reg_addr)
{
    uint8_t value;
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    // 写入寄存器地址
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (TCA9554_SENSOR_ADDR << 1) | I2C_MASTER_WRITE, true);
    i2c_master_write_byte(cmd, reg_addr, true);

    // 读取数据
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (TCA9554_SENSOR_ADDR << 1) | I2C_MASTER_READ, true);
    i2c_master_read_byte(cmd, &value, true);
    i2c_master_stop(cmd);

    ESP_RETURN_ON_ERROR(i2c_master_cmd_begin(I2C_MASTER_PORT, cmd, 1000 / portTICK_PERIOD_MS), TAG, "I2C read_reg failed");
    i2c_cmd_link_delete(cmd);

    return value;
}

esp_tca9554_io_level_t tca9554_get_input_state(esp_tca9554_gpio_num_t gpio_num)
{
    char data = 0;
    if (gpio_num < TCA9554_GPIO_NUM_MAX)
    {
        data = tca9554_read_reg(TCA9554_INPUT_PORT);
    }
    else
    {
        ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
        return TCA9554_LEVEL_ERROR;
    }

    return GET_BITS(data, gpio_num);
}

esp_tca9554_io_level_t tca9554_get_output_state(esp_tca9554_gpio_num_t gpio_num)
{
    char data = 0;
    if (gpio_num < TCA9554_GPIO_NUM_MAX)
    {
        data = tca9554_read_reg(TCA9554_OUTPUT_PORT);
    }
    else
    {
        ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
        return TCA9554_LEVEL_ERROR;
    }

    return GET_BITS(data, gpio_num);
}

esp_err_t tca9554_set_output_state(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_level_t level)
{
    char data;
    esp_err_t res = ESP_FAIL;
    if (gpio_num < TCA9554_GPIO_NUM_MAX)
    {
        data = tca9554_read_reg(TCA9554_OUTPUT_PORT);
        res = tca9554_write_reg(TCA9554_OUTPUT_PORT, SET_BITS(data, gpio_num, level));
    }
    else
    {
        ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
    }
    return res;
}

esp_err_t tca9554_set_polarity_inversion(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_polarity_t polarity)
{
    char data;
    esp_err_t res = ESP_FAIL;
    if (gpio_num < TCA9554_GPIO_NUM_MAX)
    {
        data = tca9554_read_reg(TCA9554_POLARITY_INVERSION_PORT);
        res = tca9554_write_reg(TCA9554_POLARITY_INVERSION_PORT, SET_BITS(data, gpio_num, polarity));
    }
    else
    {
        ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
    }
    return res;
}

esp_tca9554_io_config_t tca9554_get_io_config(esp_tca9554_gpio_num_t gpio_num)
{
    char data = 0;
    if (gpio_num < TCA9554_GPIO_NUM_MAX)
    {
        data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT);
    }
    else
    {
        ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
        return TCA9554_LEVEL_ERROR;
    }

    return GET_BITS(data, gpio_num);
}

esp_err_t tca9554_set_io_config(esp_tca9554_gpio_num_t gpio_num, esp_tca9554_io_config_t io_config)
{
    char data;
    esp_err_t res = ESP_FAIL;
    if (gpio_num < TCA9554_GPIO_NUM_MAX)
    {
        data = tca9554_read_reg(TCA9554_CONFIGURATION_PORT);
        res = tca9554_write_reg(TCA9554_CONFIGURATION_PORT, SET_BITS(data, gpio_num, io_config));
    }
    else
    {
        ESP_LOGE(TAG, "gpio num is error, current gpio: %d", gpio_num);
    }
    return res;
}

void tca9554_read_all()
{
    for (int i = 0; i < 0x04; i++)
    {
        uint8_t reg = tca9554_read_reg(i);
        ESP_LOGI(TAG, "REG:%02x, %02x", i, reg);
    }
}

esp_err_t tca9554_init(void)
{
    esp_err_t ret = ESP_OK;
    my_i2c_init();
    return ret;
}

esp_err_t tca9554_deinit()
{

    return ESP_OK;
}
